package xyz.larkin.common.api.impl;

import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by liujin on 2018/12/21.
 *
 * @description TODO
 */
public class SftpUtils  {
    private static Logger logger = LoggerFactory.getLogger(SftpUtils.class);

    private static final int SESSION_TIMEOUT = 10000;
    private static Map<String, String> passMap = new HashMap<>();
    private static Map<String, String> keyMap = new HashMap<>();
    static {
        try {
            PropertiesFactoryBean factoryBean = new PropertiesFactoryBean();
            factoryBean.setSingleton(false);
            factoryBean.setLocation(new PathMatchingResourcePatternResolver().getResource("config/env.properties"));
            Properties properties = Optional.ofNullable(factoryBean.getObject()).orElse(new Properties());
            logger.info("init sftp data...");
            //1.初始化passMap
            String pass = properties.getProperty("sftp.pass");
            String[] passes = pass.split(",");
            Arrays.asList(passes).stream().forEach(keyAndValue -> {
                String[] keyValue = keyAndValue.split(":");
                passMap.put(keyValue[0], keyValue[1]);
                logger.debug("{}:{}", keyValue[0], keyValue[1]);
            });
            //2.初始化keyMap
            String file = properties.getProperty("sftp.key.file");
            String[] files = file.split(",");
            Arrays.stream(files).forEach(keyAndPath -> {
                String[] keyPath = keyAndPath.split(":");
                keyMap.put(keyPath[0], keyPath[1]);
                logger.debug("{}:{}", keyPath[0], keyPath[1]);
            });
            logger.info("init sftp data end...");
        } catch (IOException e) {
            logger.error("sftp获取密钥失败");
        }
    }

    public static boolean uploadFile(String remoteUrl, String localFilePath) {
        boolean result = false;
        try {
            result = transferFile(remoteUrl, localFilePath, true);
        } catch (SftpException e) {
            logger.error("sftp exception", e);
        } catch (JSchException e) {
            logger.error("sftp exception", e);
        }
        return true;
    }
    public static boolean downloadFile(String remoteUrl, String localFilePath) {
        boolean result = false;
        try {
            result = transferFile(remoteUrl, localFilePath, false);
        } catch (SftpException e) {
            logger.error("sftp exception", e);
        } catch (JSchException e) {
            logger.error("sftp exception", e);
        }
        return true;
    }

    private static boolean transferFile(String remoteUrl, String localPath, boolean isUpload) throws SftpException, JSchException {
        if (StringUtils.isEmpty(remoteUrl) || StringUtils.isEmpty(localPath) ||
                !remoteUrl.startsWith("sftp://") ) {
            logger.error("sftp://username@url 格式有误, url:{}, localPath:{}", remoteUrl, localPath);
            return false;
        }
        //sftp://sss@sds.xyz.xx.com:2222/channel/file
        //String regex = "^sftp://(\\w+)@([^\\s^:^/]+):?([0-9]+)?((/\\w*)+)$";
        //sftp://bcssuser@10.239.11.41/channelFile/dm/sad.txt
        String regex = "^sftp://(\\w+)@([^\\s^:^/]+):?([0-9]+)?(/+(\\w+/+)+)?/*(\\w+.\\w+)?$";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(remoteUrl);
        if(!matcher.matches() && matcher.groupCount()!=3) {
            return false;
        }
        String userName = matcher.group(1);
        String host = matcher.group(2);
        String port = Optional.ofNullable(matcher.group(3)).orElse("22");
        String filePath = Optional.ofNullable(matcher.group(4)).orElse("/");
        String fileName = Optional.ofNullable(matcher.group(6)).orElse("");


        String pass = passMap.get(host+"-"+userName);
        String key = keyMap.get(host+"-"+userName);

        JSch jsch = null;
        Session session = null;
        Channel channel = null;
        ChannelSftp c = null;
        try {
            jsch = new JSch();
            //添加keyFile
            if (!StringUtils.isEmpty(key)) {
                jsch.addIdentity(key);
            }
            session = jsch.getSession(userName, host, Integer.parseInt(port));
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);
            session.setPassword(pass);
            session.setTimeout(SESSION_TIMEOUT);
            logger.debug("jsch session connection host:{}, port:{}", host, port);
            session.connect();
            logger.debug("jsch session connection connected host:{}, port:{}", host, port);

            channel = session.openChannel("sftp");
            channel.connect();
            c = (ChannelSftp) channel;

            logger.debug("jsch channel connection connected  host:{}, port:{}", host, port);
            if (isUpload) {
                logger.debug("Starting File upload, url" + remoteUrl + " localPath:" + localPath);
                //c.put(localPath, remotePath, new MySftpProgressMonitor(remoteUrl), ChannelSftp.RESUME);
                if(!isDirExist(c , filePath)){
                    createDir(filePath.substring(0, filePath.lastIndexOf("/")), c);
                }
                c.put(localPath, filePath+fileName, new MySftpProgressMonitor(remoteUrl), ChannelSftp.OVERWRITE);

            } else {
                logger.debug("Starting File download, url" + remoteUrl + " localPath:" + localPath);
                //c.get(remotePath, localPath, new MySftpProgressMonitor(remoteUrl), ChannelSftp.RESUME);
                c.get(filePath, localPath, new MySftpProgressMonitor(remoteUrl), ChannelSftp.OVERWRITE);
            }
        } catch (SftpException sftpException) {
            logger.error("sftpException id:" + sftpException.id + ", ", sftpException);
            throw sftpException;
        } catch (JSchException jSchException) {
            logger.error("jSchException,", jSchException);
            throw jSchException;
        } catch (Exception e) {
            logger.error("transfer file failed, ", e);
            throw new RuntimeException(e);
        } finally {
            if (c != null) {
                c.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
        }
        return true;
    }

    /**
     * 判断目录是否存在
     */
    private static boolean isDirExist(ChannelSftp sftp, String directory) {
        boolean isDirExistFlag = false;
        try {
            SftpATTRS sftpATTRS = sftp.lstat(directory);
            isDirExistFlag = true;
            return sftpATTRS.isDir();
        } catch (Exception e) {
            if (e.getMessage().toLowerCase().equals("no such file")) {
                isDirExistFlag = false;
            }
        }
        return isDirExistFlag;
    }

    /**
     * 创建一个文件目录
     */
    public static void createDir(String createPath, ChannelSftp sftp) throws SftpException {
        if (isDirExist(sftp, createPath)) {
            return;
        }
        String pathArray[] = createPath.split("/");
        StringBuffer filePath = new StringBuffer("/");
        for (String path : pathArray) {
            if (path.equals("")) {
                continue;
            }
            filePath.append(path + "/");
            if (!isDirExist(sftp, filePath.toString())) {
                // 建立目录
                sftp.mkdir(filePath.toString());
            }
        }
    }

    private static class MySftpProgressMonitor implements SftpProgressMonitor{
        private long alreadyTransfer = 0;
        private String remoteUrl;

        public MySftpProgressMonitor(String remoteUrl) {
            this.remoteUrl = remoteUrl;
        }

        @Override
        public void init(int index, String src, String dest, long max) {

            logger.info("sftp transfer begin. from:{},to:{}, start:{}, total:{}", src, dest, index, max);
        }

        @Override
        public boolean count(long l) {
            alreadyTransfer += l;
            logger.info("{} sftp currently transferred size:{} bytes.", remoteUrl, alreadyTransfer);
            return true;
        }

        @Override
        public void end() {
            logger.info("sftp transfer done.");
        }
    }
}
